package de.zainodis.commons.communication.local;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;

import java.util.HashSet;
import java.util.Set;

/**
 * Can be created to register {@link Callback}s. Upon being called, the
 * {@link CallbackPort} executes each subscribed callback.
 * 
 * @author zainodis
 * 
 */
public class CallbackPort {

   private Class<?>[] parameters;

   private Set<Callback> subscribers;

   /**
    * The definition of parameters is required, so {@link Callback}s know which
    * parameter's they may process in which order.
    * 
    * @param parameters
    *           the list of parameters, that the callback port will pass along
    *           upon calling {@link Callback#execute(Object...)}.
    */
   public CallbackPort(Class<?>... parameters) {
	 assertNotNull("Parameters defined by a callback port must not be null.", parameters);
	 this.parameters = parameters;
   }

   public void subscribe(Callback callback) {
	 if (callback != null) {
	    getSubscribers().add(callback);
	 }
   }

   public void unsubscribe(Callback callback) {
	 getSubscribers().remove(callback);
   }

   public void send(Object... arguments) {
	 if (arguments == null) {
	    arguments = new Object[0];
	 }
	 // Assert that the arguments match the parameters defined by the ports
	 assertEquals("Number of declared parameters must match number of passed arguments.",
		  arguments.length, parameters.length);

	 Set<Callback> toBeDeleted = new HashSet<Callback>();

	 for (Callback callback : getSubscribers()) {
	    callback.execute(arguments);
	    if (callback.isAutoUnsubscribe()) {
		  // Schedule for deletion
		  toBeDeleted.add(callback);
	    }
	 }

	 // Delete all marked entries
	 getSubscribers().removeAll(toBeDeleted);
   }

   /**
    * @return the number of subscribers.
    */
   public int getSubscriberCount() {
	 return getSubscribers().size();
   }

   /**
    * Removes all currently active subscriptions.
    */
   public void clearSubscriptions() {
	 getSubscribers().clear();
   }

   private Set<Callback> getSubscribers() {
	 if (subscribers == null) {
	    subscribers = new HashSet<Callback>();
	 }
	 return subscribers;
   }
}
